﻿using System;
using System.Collections.Generic;
using System.Linq;
using Artemis;
using libtcod;

namespace RL
{
    public class GameMap : Component
    {
        private int _width, _height;
        private TCODMap _tcodMap;
        private Stack<IRenderable>[,] _tiles;
        public TCODConsole MapConsole { get; set; }

        public GameMap(int width, int height)
        {
            _width = width;
            _height = height;
            _tcodMap = new TCODMap(width, height);
            
            MapConsole = new TCODConsole(width, height);

            Initialise();
        }

        private void Initialise()
        {
            var heightMap = InitHeightMap(Math.Max(_width, _height));
            
            _tiles = new Stack<IRenderable>[_width, _height];

            float min = int.MaxValue, max = 0;
            foreach (int x in Enumerable.Range(0, _width))
            {
                foreach (var y in Enumerable.Range(0, _height))
                {
                    _tiles[x, y] = new Stack<IRenderable>();
                    float rawHeight = heightMap.Heights[x, y] + 0.5f;
                    min = Math.Min(rawHeight, min);
                    max = Math.Max(rawHeight, max);
                    int terrainHeight = (int)(rawHeight * 256) % 255;
                    if (terrainHeight < 100)
                    {
                        _tiles[x, y].Push(new Terrain(new Point(x, y), ' ', TerrainType.Water, terrainHeight));
                        _tcodMap.setProperties(x, y, true, false);
                    }
                    else if (terrainHeight < 200)
                    {
                        _tiles[x, y].Push(new Terrain(new Point(x, y), ' ', TerrainType.Grassland, terrainHeight));
                        _tcodMap.setProperties(x, y, true, true);
                    }
                    else
                    {
                        _tiles[x, y].Push(new Terrain(new Point(x, y), ' ', TerrainType.Mountain, terrainHeight));
                        _tcodMap.setProperties(x, y, false, false);
                    }

                    Draw(x, y);
                }
            }

            Console.WriteLine("Min: {0}, Max: {1}", min, max);
        }

        private static HeightMap InitHeightMap(int size)
        {
            var map = new HeightMap(size);
            map.AddPerlinNoise(6.0f);
            map.Perturb(32.0f, 32.0f);
            for (int i = 0; i < 10; i++)
                map.Erode(16.0f);
            map.Smoothen();
            return map;
        }

        private void Draw(int x, int y)
        {
            var obj = _tiles[x, y].Peek();
            var tile = _tiles[x, y].Last();
            
            if (_tcodMap.isInFov(x, y))
            {
                MapConsole.putCharEx(x, y, obj.Model, obj.Color, tile.Color);                
            } 
            else
            {
                MapConsole.putCharEx(x, y, obj.Model, obj.Color, tile.Color.Fog());
            }
        }

        //public void Move(IEntity entity, MoveType type)
        //{
        //    var body = entity.Body;
        //    body.LastPosition = body.Position;
        //    switch (type)
        //    {
        //        case MoveType.Up:
        //            body.Position.Y -= 1;
        //            break;
        //        case MoveType.Down:
        //            body.Position.Y += 1;
        //            break;
        //        case MoveType.Left:
        //            body.Position.X -= 1;
        //            break;
        //        case MoveType.Right:
        //            body.Position.X += 1;
        //            break;
        //    }
        //    if (IsCollisionDetected(body))
        //    {
        //        body.Position =body.LastPosition;
        //        return;
        //    }
        //    var x = body.Position.X;
        //    var y = body.Position.Y;
        //    var prevX = body.LastPosition.X;
        //    var prevY = body.LastPosition.Y;
        //    try
        //    {
        //        MapConsole.putCharEx(x, y, entity.Model, entity.Color, _tiles[x, y].Last().Color);
        //        MapConsole.putCharEx(prevX, prevY, ' ', _tiles[prevX, prevY].Last().Color, _tiles[prevX, prevY].Last().Color);
        //    }
        //    catch (IndexOutOfRangeException)
        //    {
        //        body.Position = body.LastPosition;
        //        return;
        //    }
        //}

        //private bool IsCollisionDetected(IBody target)
        //{
        //    if (target.Position.X < 0 || target.Position.X >= _width ||
        //        target.Position.Y < 0 || target.Position.Y >= _height) return true;

        //    if (((Terrain)_tiles[target.Position.X, target.Position.Y].Last()).Type != TerrainType.Grassland)
        //        return true;

        //    foreach (var body in Bodies)
        //    {
        //        if (body == target) continue;

        //        if (body.Position.X == target.Position.X)
        //            if (body.Position.Y == target.Position.Y)
        //                return true;
        //    }
        //    return false;
        //}s

        //public void Refresh(Rect window)
        //{
        //    _tcodMap.computeFov(Following.Position.X, Following.Position.Y);
        //    var xMin = window.TopLeft.X.Bound(0, _width - 1);
        //    var xMax = (window.TopLeft.X + window.Width).Bound(0, _width - 1);
        //    var yMin = window.TopLeft.Y.Bound(0, _height - 1);
        //    var yMax = (window.TopLeft.Y + window.Height).Bound(0, _height - 1);

        //    foreach (int x in Enumerable.Range(xMin, xMax-xMin))
        //    {
        //        foreach (int y in Enumerable.Range(yMin, yMax-yMin))
        //        {
        //            Draw(x, y);                    
        //        }
        //    }

        //    Entities.ForEach(x =>
        //                         {
        //                             if (_tcodMap.isInFov(x.X, x.Y))
        //                             {
        //                                 MapConsole.putCharEx(x.X, x.Y, x.Model, x.Color, _tiles[x.X, x.Y].Last().Color);
        //                             }
        //                             else
        //                             {
        //                                 MapConsole.putCharEx(x.X, x.Y, x.Model, x.Color, _tiles[x.X, x.Y].Last().Color.Fog());
        //                             }
        //                         });
        //}
    }

    public static class Extensions
    {
        public static int Bound(this int i, int lower, int upper)
        {
            return Math.Min(Math.Max(i, lower), upper);
        }

        public static TCODColor Fog(this TCODColor col)
        {
            return new TCODColor(col.getHue(), col.getSaturation(), col.getValue()*0.5f);
        }
    }
}